/*
 *  sparticlus.cpp
 *  sparticlus
 *
 *  Created by Lita Gratrix.
 *  Copyright 2010 Lita Gratrix Development. All rights reserved.
 *
 */ 

#include <stdio.h>
#include <sstream>
#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MStringArray.h>
#include <maya/MObject.h>
#include <maya/MObjectArray.h>
#include <maya/MVector.h>
#include <maya/MVectorArray.h>
#include <maya/MArgList.h>
#include <maya/MFnPlugin.h>
#include <maya/MPxCommand.h>
#include <maya/MIOStream.h>
#include <maya/MSyntax.h>
#include <maya/MArgDatabase.h>
#include <maya/MItDag.h>
#include <maya/MFn.h>
#include <maya/MDagPath.h>
#include <maya/MSelectionList.h>
#include <maya/MCommandResult.h>
#include <maya/MFnDagNode.h>
#include <maya/MFnParticleSystem.h>

#include "sparticlusSimulation.h"

////////////////////////////////////
//Syntax String Definitions
////////////////////////////////////
#define kSpartStartFrameFlag "-sf"
#define kSpartStartFrameLongFlag "-startFrame"
#define kSpartFinishFrameFlag "-ff"
#define kSpartFinishFrameLongFlag "-finishFrame"
#define kSpartSampleRateFlag "-sr"
#define kSpartSampleRateLongFlag "-sampleRate"
#define kSpartCacheOutFlag "-co"
#define kSpartCacheOutLongFlag "-cacheOut"
#define kSpartCacheDirectoryFlag "-cd"
#define kSpartCacheDirectoryLongFlag "-cacheDirectory"
#define kSpartParticleFlag "-p"
#define kSpartParticleLongFlag "-particle"


/////////////////////////////////////
//The sparticlusRunSimulation Command
/////////////////////////////////////
class sparticlusRunSimulation : public MPxCommand 
{
public:
	MStatus doIt( const MArgList& args );
	static MSyntax runSimSyntax();
	static void* creator();
	
private:
	MStatus parseArgs( const MArgList& args);
	
private:
	int startFrame, finishFrame;
	int sampleRate;
	bool cacheOutFlag;
	MString cacheDir;
	MObjectArray particles;
};

//The main sparticlus command, collects data from the maya scene
//and passes it to simulation for pre-CUDA processing 
MStatus sparticlusRunSimulation::doIt( const MArgList& args ) {
	MGlobal::displayInfo("Entering sparticlusRunSimulation");
	MStatus status = parseArgs( args );
	if( status != MStatus::kSuccess ){
		return status;
	}

	//Debug print statements
	/*
	cout << "Sparticlus Run Simulation" << endl;
	cout << "You've selected to simulate data for ";
	int i;
	int numParticles = particles.length();
	for (i = 0; i < numParticles; i++){
		if(i > 0){
			cout << ", ";
		}
		MFnParticleSystem tempPartSystem(particles[i]);
		cout << tempPartSystem.particleName();
		
	}
	cout << ".\n";
	 
	if (cacheOutFlag == 0){
		cout << "You are not cacheing out. " << endl;
	} else {
		cout << "You are cacheing out." <<endl;
	}
	cout << "Start Frame is: " << startFrame << endl;
	cout << "Finish Frame is: " << finishFrame << endl;
	cout << "Sample Rate is: " << sampleRate << endl;
	*/
	
	sparticlusSimulation sim;
	sim.simulate(particles, startFrame, finishFrame, sampleRate);
	
 	return MS::kSuccess;
}

//constructor
void* sparticlusRunSimulation::creator() {
	return new sparticlusRunSimulation;
}

//esablishes the accetable flags for the sparticlusRunSimulation command
MSyntax sparticlusRunSimulation::runSimSyntax(){
	//MGlobal::displayInfo("Entering runSimSyntax");
	MSyntax syntax;
	syntax.addFlag(kSpartStartFrameFlag, kSpartStartFrameLongFlag, MSyntax::kDouble);
	syntax.addFlag(kSpartFinishFrameFlag, kSpartFinishFrameLongFlag, MSyntax::kDouble);
	syntax.addFlag(kSpartSampleRateFlag, kSpartSampleRateLongFlag, MSyntax::kDouble);
	syntax.addFlag(kSpartCacheOutFlag, kSpartCacheOutLongFlag, MSyntax::kBoolean);
	syntax.addFlag(kSpartCacheDirectoryFlag, kSpartCacheDirectoryLongFlag, MSyntax::kString);
	syntax.setObjectType(MSyntax::kSelectionList, 1);
	//MGlobal::displayInfo("Exiting runSimSyntax");
	return syntax;
}

//parses the command flags and their arguments
MStatus sparticlusRunSimulation::parseArgs( const MArgList& args){
	//MGlobal::displayInfo("Entering parseArgs");
	MStatus status = MS::kSuccess;
	
	//check the flags are valid, and determine the number of selected particles
	MSyntax syntax = runSimSyntax();
	MArgDatabase argData(syntax, args);
	
	MObjectArray sceneParts;
	MDagPath partDagPath;

	MSelectionList selParts;
	argData.getObjects(selParts);
	
	//Debug print statements
	/*
	MString info = "number of selParts is ";
	info+= selParts.length();
	displayInfo(info);
	*/
	
	if(selParts.length() == 0){
		displayError("No particles selected.");
		return MStatus::kFailure;
	}
	
	int i;
	for(i = 0; i < selParts.length(); i++){
		displayInfo("getting dependancy node of selectionList objects");
		MObject temp;
		selParts.getDependNode(i, temp );
		particles.append( temp );
	}

	//parse start and finish frames
	if(argData.isFlagSet(kSpartStartFrameFlag)){
		argData.getFlagArgument(kSpartStartFrameFlag, 0, startFrame);
	} else {
		MGlobal::displayError ("Missing required flag -startFrame");
		return MS::kFailure;
	}
	
	if(argData.isFlagSet(kSpartFinishFrameFlag)){
		argData.getFlagArgument(kSpartFinishFrameFlag, 0, finishFrame);
	} else {
		MGlobal::displayError ("Missing required flag -finishFrame");
		return MS::kFailure;
	}
	
	//parse sample data
	if(argData.isFlagSet(kSpartSampleRateFlag)){
		argData.getFlagArgument(kSpartSampleRateFlag, 0, sampleRate);
	} else {
		sampleRate = 1;
	}
	
	//sample rate must be greater than 1
	if(sampleRate < 1){
		MGlobal::displayError("Sample must be 1 or greater");
		return MS::kFailure;
	}
	
	//parse cacheOutFlag
	if(argData.isFlagSet(kSpartCacheOutFlag)){
		argData.getFlagArgument(kSpartCacheOutFlag, 0, cacheOutFlag);
	} else {
		cacheOutFlag = 0;
	}
	
	if(argData.isFlagSet(kSpartCacheDirectoryFlag)){
		argData.getFlagArgument(kSpartCacheDirectoryFlag, 0, cacheDir);
	}
	
	return MS::kSuccess;
}

////////////////////////////////////////////
//The sparticlus plugin maintenance methods
////////////////////////////////////////////
MStatus initializePlugin( MObject obj ) {
	MStatus status;
	
	status = MGlobal::sourceFile( "sparticlusGUI" );
	if (!status){
		MGlobal::displayError("Error sourcing sparticlusGUI");
	}
	
	MFnPlugin plugin( obj, "Lita Gratrix Developement", "1.0", "Any" );
	
	status = plugin.registerCommand( "sparticlusRunSimulation", sparticlusRunSimulation::creator, sparticlusRunSimulation::runSimSyntax );
	if (!status) {
		MGlobal::displayError("Error registering sparticlusRunSimulation command");
		return status;
	} else {
		cout << "Sparticlus loaded successfully" << endl;
	}
				 
	MGlobal::executeCommand( "addSparticlusMenuItem()" );
	return status;
}

MStatus uninitializePlugin( MObject obj ) {
	MFnPlugin plugin( obj );
	MGlobal::executeCommand( "removeSparticlusGUI()" );
	MStatus status = plugin.deregisterCommand( "sparticlusRunSimulation" );
	if(!status) {
		MGlobal::displayError("Error de-registering sparticlusRunSimulation command");
	} else {
		cout << "Sparticlus unloaded successfully" << endl;
	}	
	return MS::kSuccess;
}


